package cn.wjssl.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import cn.wjssl.domain.credit.model.aggregate.TradeAggregate;
import cn.wjssl.domain.credit.model.entity.CreditAccountEntity;
import cn.wjssl.domain.credit.model.entity.CreditOrderEntity;
import cn.wjssl.domain.credit.model.entity.TaskEntity;
import cn.wjssl.domain.credit.model.valobj.TradeNameVO;
import cn.wjssl.domain.credit.model.valobj.TradeTypeVO;
import cn.wjssl.domain.credit.repository.ICreditRepository;
import cn.wjssl.infrastructure.event.EventPublisher;
import cn.wjssl.infrastructure.persistent.dao.ITaskDao;
import cn.wjssl.infrastructure.persistent.dao.IUserCreditAccountDao;
import cn.wjssl.infrastructure.persistent.dao.IUserCreditOrderDao;
import cn.wjssl.infrastructure.persistent.po.TaskPO;
import cn.wjssl.infrastructure.persistent.po.UserCreditAccountPO;
import cn.wjssl.infrastructure.persistent.po.UserCreditOrderPO;
import cn.wjssl.infrastructure.persistent.redis.IRedisService;
import cn.wjssl.types.common.Constants;
import cn.wjssl.types.enums.ResponseCode;
import cn.wjssl.types.exception.AppException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

/**
 * 积分相关仓储
 */
@Slf4j
@Repository
public class CreditRepository implements ICreditRepository {

    @Resource
    private IUserCreditAccountDao creditAccountDao;
    @Resource
    private IUserCreditOrderDao creditOrderDao;
    @Resource
    private ITaskDao taskDao;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private IRedisService redisService;

    @Resource
    private IDBRouterStrategy dbRouter;

    @Resource
    private EventPublisher eventPublisher;

    /**
     * 创建积分操作单, 更新积分账户, 推送sku支付成功mq, 更新sku单
     * @param tradeAggregate
     */
    @Override
    public void saveUserCreditTradeOrder(TradeAggregate tradeAggregate) {
        String userId = tradeAggregate.getUserId();
        CreditAccountEntity creditAccountEntity = tradeAggregate.getCreditAccountEntity();
        CreditOrderEntity creditOrderEntity = tradeAggregate.getCreditOrderEntity();
        TaskEntity taskEntity = tradeAggregate.getTaskEntity();

        // 1. 构建积分账户PO, 总积分和可用积分, 都是增加这一次调额的积分值
        UserCreditAccountPO creditAccountPO = new UserCreditAccountPO();
        creditAccountPO.setUserId(userId);
        creditAccountPO.setTotalAmount(creditAccountEntity.getAdjustAmount());
        creditAccountPO.setAvailableAmount(creditAccountEntity.getAdjustAmount());

        // 2. 构建积分订单PO
        UserCreditOrderPO creditOrderPO = new UserCreditOrderPO();
        creditOrderPO.setUserId(creditOrderEntity.getUserId());
        creditOrderPO.setOrderId(creditOrderEntity.getOrderId());
        creditOrderPO.setTradeName(creditOrderEntity.getTradeName().getName());
        creditOrderPO.setTradeType(creditOrderEntity.getTradeType().getCode());
        creditOrderPO.setTradeAmount(creditOrderEntity.getTradeAmount());
        creditOrderPO.setOutBusinessNo(creditOrderEntity.getOutBusinessNo());   // lxd_integral_20250311

        // 3. 构建taskPO
        TaskPO taskPO = new TaskPO();
        taskPO.setUserId(taskEntity.getUserId());
        taskPO.setTopic(taskEntity.getTopic());
        taskPO.setMessageId(taskEntity.getMessageId());
        taskPO.setMessage(JSON.toJSONString(taskEntity.getMessage()));
        taskPO.setState(taskEntity.getState().getCode());

        /**
         * 4. 对 userId, outBusinessNo 加锁
         * 保存积分调额订单, 更新用户积分账户积分,
         */
        RLock lock = redisService.getLock(Constants.RedisKey.USER_CREDIT_ACCOUNT_LOCK
                + userId
                + Constants.UNDERLINE
                + creditOrderEntity.getOutBusinessNo()
        );
        try {
            lock.lock(3, TimeUnit.SECONDS);
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try {
                    // 1. 存入积分调额订单
                    creditOrderDao.insert(creditOrderPO);
                    // 2. 积分账户调额
                    UserCreditAccountPO creditAccountPORes = creditAccountDao.queryUserCreditAccount(creditAccountPO);
                    if (creditAccountPORes == null) {   // 不存在积分账户, 新增, 不然就更新
                        creditAccountDao.insert(creditAccountPO);
                    } else {
                        /**
                         * 这里对积分调额单的金额进行判断
                         * 如果金额 > 0, 那就调用增加积分接口
                         * 如果金额 < 0, 说明是sku支付单, 那就调用扣减积分接口
                         */
                        BigDecimal availableAmount = creditAccountPORes.getAvailableAmount();
                        if (availableAmount.compareTo(BigDecimal.ZERO) >= 0) {
                            // 订单金额 > 0, 增加积分
                            creditAccountDao.updateAddAmount(creditAccountPO);
                        } else {
                            // 订单金额 < 0, 是sku支付单, 在sql中判断余额是否>0
                            int i = creditAccountDao.updateSubtractionAmount(creditAccountPO);
                            if (1 != i) {
                                status.setRollbackOnly();
                                throw new AppException(ResponseCode.USER_CREDIT_ACCOUNT_NO_AVAILABLE_AMOUNT.getCode(), ResponseCode.USER_CREDIT_ACCOUNT_NO_AVAILABLE_AMOUNT.getInfo());
                            }
                        }
                    }
                    // 3. 插入task记录
                    if (!creditOrderEntity.getTradeName().getName().equals(TradeNameVO.REBATE.getName())) {
                        taskDao.insert(taskPO);
                    }

                }catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("调整账户积分额度异常，唯一索引冲突 userId:{} orderId:{}", userId, creditOrderEntity.getOrderId(), e);
                } catch (Exception e) {
                    status.setRollbackOnly();
                    log.error("调整账户积分额度失败 userId:{} orderId:{}", userId, creditOrderEntity.getOrderId(), e);
                }
                return 1;
            });
        }finally {
            dbRouter.clear();
            lock.unlock();
        }

        if (!creditOrderEntity.getTradeName().getName().equals(TradeNameVO.REBATE.getName())) {
            // 事务成功, 推送积分调额成功信息
            try {
                eventPublisher.publish(taskPO.getTopic(), taskPO.getMessage());
                taskDao.updateTaskSendMessageCompleted(taskPO);
                log.info("调整账户积分记录，发送MQ消息完成 userId: {} orderId:{} topic: {}", userId, creditOrderEntity.getOrderId(), taskPO.getTopic());
            } catch (Exception e) {
                log.error("调整账户积分记录，发送MQ消息失败 userId: {} topic: {}", userId, taskPO.getTopic());
                taskDao.updateTaskSendMessageFail(taskPO);
            }
        }
    }

    // 根据 userId  查询积分账户余额
    @Override
    public CreditAccountEntity queryUserCreditAccount(String userId) {
        UserCreditAccountPO creditAccountPO = new UserCreditAccountPO();
        creditAccountPO.setUserId(userId);
        UserCreditAccountPO creditAccountPORes = creditAccountDao.queryUserCreditAccountByUserId(creditAccountPO);
        // 如果账户不存在, 返回余额为0  
        BigDecimal availableAmount = BigDecimal.ZERO;
        if (null != creditAccountPORes) {
            availableAmount = creditAccountPORes.getAvailableAmount();
        }
        return CreditAccountEntity.builder()
                .userId(userId)
                .adjustAmount(availableAmount)
                .build();
    }
}
